#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/input-event-codes.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/uaccess.h>
#define CNAME "beeper_driver"
#define HZ_TO_NANOSECONDS(x) (1000000000UL / (x)) 
int major;
struct class *cls;
struct pwm_beeper {
  struct pwm_device *pwm;
  unsigned long period;
};
struct pwm_beeper *beeper;
int beeper_open(struct inode *inode, struct file *file) {

  printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
  return 0;
}
ssize_t beeper_write(struct file *file, const char __user *ubuf, size_t size,
                     loff_t *offs) {
  int ret;
  int value;
  ret = copy_from_user(&value, ubuf, sizeof(value));
  if (ret < 0) {
    printk("copy from user error\n");
    return -EIO;
  }
  if (value == 0) {
    pwm_disable(beeper->pwm); //
  } else {
    beeper->period = HZ_TO_NANOSECONDS(value);
    ret = pwm_config(beeper->pwm, beeper -> period / 2, beeper->period);
    if (ret) {
      printk("pwm config error\n");
      return ret;
    }
    ret = pwm_enable(beeper->pwm); //
    if (ret) {
      printk("pwm enable error\n");
      return ret;
    }
  }
  return size;
}
int beeper_close(struct inode *inode, struct file *file) {

  printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
  printk("beep_close\n");
  pwm_disable(beeper->pwm);
  return 0;
}
const struct file_operations fops = {
    .open = beeper_open,
    .write = beeper_write,
    .release = beeper_close,
};
int beeper_probe(struct platform_device *pdev) {
  struct device *dev = &pdev->dev;
  int errmsg;
  beeper = kzalloc(sizeof(*beeper), GFP_KERNEL);
  if (!beeper) {
    printk("beeper kzalloc error\n");
    return -ENOMEM;
  }
  beeper->pwm = pwm_get(dev, NULL);
  if (IS_ERR(beeper->pwm)) {
 printk("failed to request pwm device:%d\n",PTR_ERR(beeper->pwm));
  }
  printk("+++++++test1========\n");

  major = register_chrdev(0, CNAME, &fops);
  if (major < 0) {
    printk("register chrdev error\n");
    return major;
  }
  printk("+++++++test1========\n");
  cls = class_create(THIS_MODULE, CNAME);
  if (IS_ERR(cls)) {
    printk("class create error\n");
    return PTR_ERR(cls);
  }
  printk("+++++++test2========\n");
  dev = device_create(cls, NULL, MKDEV(major, 0), NULL, CNAME);
  if (IS_ERR(dev)) {
    printk("device create error\n");
    return PTR_ERR(dev);
  }
  printk("+++++++test3========\n");
  return 0;
}
int beeper_remove(struct platform_device *pdev) {
  device_destroy(cls, MKDEV(major, 0));
  class_destroy(cls);
  unregister_chrdev(major, CNAME);
  if (NULL != beeper) {
    pwm_disable(beeper->pwm);
    pwm_free(beeper->pwm);
  }
  kfree(beeper);
  return 0;
}
const struct of_device_id ofmatch[] = {{
                                           .compatible = "pwm-beeper",
                                       },
                                       {}};
struct platform_driver beeper_pwm = {.probe = beeper_probe,
                                     .remove = beeper_remove,
                                     .driver = {
                                         .name = "beeper",
                                         .of_match_table = ofmatch,
                                     }};
MODULE_DEVICE_TABLE(of, ofmatch);
module_platform_driver(beeper_pwm);
MODULE_LICENSE("GPL");